#ifndef PPU_H
#define PPU_H
#include "pch.h"
#include <functional>
#include <array>
#include "PictureBus.h"
#include "MainBus.h"
#include "VirtualScreen.h"
#include "PaletteColors.h"

PNE_NAMESPACE_BEGIN
// const space
const int ScanlineCycleLength = 341;
const int ScanlineEndCycle = 340;
const int VisibleScanlines = 240;
const int ScanlineVisibleDots = 256;
const int FrameEndScanline = 261;
const int AttributeOffset = 0x3C0;
// end const space

struct PPU
{
public:
    PPU(PictureBus &bus, VirtualScreen &screen);
    void Step();
    void Reset();
    // callback function like : () -> void
    void SetInterruptCallback(std::function<void(void)> callback);
    void DoDMA(const Byte *page_ptr);

    // callback mapped to CPU address space
    // address written to by the program
    void Control(Byte ctrl);
    void SetMask(Byte mask);
    void SetOAMAddress(Byte addr);  // OAM addr
    void SetDataAddress(Byte addr); // Data addr
    void SetScroll(Byte scroll);
    void SetData(Byte data); // data
    // Read by the program
    Byte GetStatus();
    Byte GetData();
    Byte GetOAMData(); // OMA data
    void SetOAMData(Byte value);

private:
    Byte readOAM(Byte addr);
    void writeOAM(Byte addr, Byte value);
    Byte read(Address addr);
    PictureBus &m_bus;
    VirtualScreen &m_screen;

    std::function<void(void)> m_vblanCallback;

    std::vector<Byte> m_spriteMemory;
    std::vector<Byte> m_scanlineSprites;

    // enum inner class
    enum class State
    {
        PreRender,
        Render,
        PostRender,
        VerticalBlank
    } m_pipelineState;

    int m_cycle;
    int m_scanline;
    bool m_evenFrame;

    bool m_vblank;
    bool m_sprZeroHit;
    bool m_spriteOverflow;

    // Registers
    Address m_dataAddress;
    Address m_tempAddress;
    Byte m_fineXScroll;
    bool m_firstWrite;
    Byte m_dataBuffer;

    Byte m_spriteDataAddress;

    // Setup flags and variables
    bool m_longSprites;
    bool m_generateInterrupt;

    bool m_greyscaleMode;
    bool m_showSprites;
    bool m_showBackground;
    bool m_hideEdgeSprites;
    bool m_hideEdgeBackground;

    enum class CharacterPage
    {
        Low,
        High
    } m_bgPage,
        m_sprPage;

    Address m_dataAddrIncrement;
    std::vector<std::vector<sf::Color>> m_pictureBuffer;
};

PNE_NAMESPACE_END // namespace

#endif // PPU_H
       // DONE